{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：描述下Scrapy框架运行的机制?\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "Scrapy 框架是一个比较通用的爬虫框架，功能完善。它在爬虫领域的地位就等同于 Django 在 web 开发的定位。如果你要面爬虫的职位，且想要知道为什么面试官要问这个问题，那么，我倒想知道面试官有什么理由不去问这个问题。\n",
    "\n",
    "答案\n",
    "从start_urls里获取第一批url并发送请求，请求由引擎交给调度器入请求队列，获取完毕后，调度器将请求队列里的请求交给下载器去获取请求对应的响应资源，并将响应交给自己编写的解析方法做提取处理，如果提取出需要的数据，则交给管道文件处理；\n",
    "如果提取出url，则继续执行之前的步骤（发送 url 请求，并由引擎将请求交给调度器入队列…)，直到请求队列里没有请求，程序结束。\n",
    "拓展\n",
    "图片描述\n",
    "\n",
    "Scrapy 框架中 有8 个主要组件：\n",
    "\n",
    "Scrapy引擎(Scrapy engine)：这是框架的核心，用来处理整个系统的数据流处理，触发事务；\n",
    "调度器(Scheduler)：接受引擎发来的请求，压入队列中，并在引擎再次请求的时候返回。这个调度器决定URL的优先队列，并且做了去重处理；\n",
    "下载器(Downloader)：用于下载网页内容, 并将网页内容返回给爬虫(Scrapy下载器是建立在twisted这个高效的异步模型上的)；\n",
    "爬虫(Spiders)：用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接，让Scrapy引擎继续抓取下一个页面；\n",
    "项目管道(Pipeline)：负责处理爬虫从网页中抽取的实体，主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后，将被发送到项目管道，并经过几个特定的次序处理数据；\n",
    "下载器中间件(Downloader Middlewares)：位于Scrapy引擎和下载器之间的中间件，主要是处理Scrapy引擎与下载器之间的请求及响应；\n",
    "爬虫中间件(Spider Middlewares)：介于Scrapy引擎和爬虫之间的中间件，主要工作是处理爬虫的响应输入和请求输出；\n",
    "调度中间件(Scheduler Middewares)：介于Scrapy引擎和调度器之间的中间件，从Scrapy引擎发送到调度的请求和响应。\n",
    "Scrapy运行流程：\n",
    "首先，引擎从调度器中取出一个链接(URL)用于接下来的抓取；\n",
    "引擎把URL封装成一个请求(Request)传给下载器，下载器把资源下载下来，并封装成应答包(Response)；\n",
    "然后，爬虫解析Response；\n",
    "若是解析出实体（Item）,则交给实体管道进行进一步的处理；\n",
    "若是解析出的是链接（URL）,则把URL交给Scheduler等待抓取。\n",
    "问题：写爬虫是用多进程好？还是多线程好？ 为什么？\n",
    "面试官想从这道题中考察面试人什么？\n",
    "这个问题主要看面试者是否知道多进程和多线程的优势所在，并且能够从实际问题去分析爬虫的爬取速率瓶颈。所以，回答这道题，多从面试官那里了解问题的关键所在，然后对症下药。\n",
    "\n",
    "答案\n",
    "IO 密集型代码(文件处理、网络爬虫等)，多线程能够有效提升效率(单线程下有 IO 操作会进行 IO 等待，造成不必要的时间浪费，而开启多线程能在线程A等待时，自动切换到线程B，可以不浪费 CPU 的资源，从而能提升程序执行效率)。在实际的数据采集过程中，既考虑网速和响应的问题，也需要考虑自身机器的硬件情况，来设置多进程或多线程。\n",
    "\n",
    "拓展\n",
    "在上一个章节中，我们提到过决定是否在Python中使用线程的经验法则是取决于将要执行的任务是CPU密集型还是IO密集型。如果答案是IO密集型，则可以使用多线程。\n",
    "\n",
    "由于存在 GIL，Python 解释器一次只能运行一个线程。如果线程正在执行某些 I/O，它将阻止等待数据变为可用（例如，从网络连接或磁盘获得），与此同时，解释器将上下文切换到另一个线程。另一方面，如果线程正在执行 CPU 密集型任务，则其他线程将不得不等待，直到解释器决定运行它们为止。\n",
    "\n",
    "网络爬网主要是一个面向 I/O 的任务，需要建立 HTTP 连接，发送请求，等待响应。获得响应后，需要花费一些 CPU 来解析它，但是除此之外，它主要是 IO 工作。因此，写爬虫，线程是合适的选择。\n",
    "\n",
    "图片描述\n",
    "\n",
    "我用 Jupyter 写了一个简单的比对单线程和多线程爬取多个网页的 notebook，每次都使用%%time功能去显示运行 Python 代码的时间。\n",
    "\n",
    "大家可以使用pip install jupyter去安装Jupyter，然后调用jupyter notebook去运行本地的jupyter服务，随后电脑中的默认浏览器会自动打开一个页面窗口。大家可以创建Python3 Notebook，在新创建的笔记本中插入Cell，每个Cell可以是代码段，也可以是markdown文本。\n",
    "\n",
    "使用多个线程可以大大加快许多受 IO 约束的任务。在这里，读取 URL 所花费的大部分时间是由于网络延迟。I/O 密集程序花费了大部分时间来等待输入/输出（类似于向导如何等待他的一个或多个朋友转到滚动条中给出的位置并返回）。这可能是来自网络，数据库，文件甚至用户的 I/O。由于源本身可能需要在传递 I/O 之前执行自己的处理，因此该 I/O 往往会花费大量时间。例如，CPU 的工作速度比网络连接传输数据的速度快得多。\n",
    "\n",
    "我们再看看多进程对 CPU 密集任务的性能影响。\n",
    "\n",
    "图片描述\n",
    "\n",
    "Tips: 感兴趣的同学们，可以动手试试去改线程数和进程数，看看性能有什么影响。\n",
    "\n",
    "小结\n",
    "随着互联网技术的发展与应用的普及，网络作为信息的载体，编写爬虫程序成为大数据时代信息收集的必备技能。这一节主要简单列举了几个主流的爬虫框架。而如何快速高效地爬取数据，仍然是爬虫最主要的问题。并且着重讲了一下 scrapy 的框架，以及它的分布式组件 scrapy-redis。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
